package cz.tomasdvorak.cars.utils;

import java.util.*;

/**
 * Pomocne metody pro praci se seznamy.
 */
public class Lists {

    private Lists() {}

    public static <T> T[] array(T... elements) {
        return elements;
    }

    /**
     * Vytvori prazdny {@link ArrayList} se zadanou pocatecni kapacitou.
     */
    public static <T> ArrayList<T> arrayList(int initialCapacity) {
        return new ArrayList<T>(initialCapacity);
    }

    /**
     * Vytvori {@link ArrayList} ze vsech zadanych hodnot, ktere nejsou {@code null}.
     * Nikdy nevraci {@code null} a nevyhazuje NPE.
     */
    public static <T> ArrayList<T> arrayList(T... values) {
        return arrayList(values == null ? Collections.<T>emptyList() : Arrays.asList(values));
    }

    /**
     * Vytvori {@link ArrayList} ze vsech zadanych hodnot, ktere nejsou {@code null}.
     * Nikdy nevraci {@code null} a nevyhazuje NPE.
     */
    public static <T> ArrayList<T> arrayList(Collection<? extends T> values) {
        ArrayList<T> result = new ArrayList<T>();
        if (values != null) {
            for (T value : values) {
                if (value != null) {
                    result.add(value);
                }
            }
        }
        return result;
    }

    /**
     * Vytvori prazdny {@link LinkedList}.
     */
    public static <T> LinkedList<T> linkedList() {
        return new LinkedList<T>();
    }

    /**
     * Vytvori {@link LinkedList} ze vsech zadanych hodnot, ktere nejsou {@code null}.
     * Nikdy nevraci {@code null} a nevyhazuje NPE.
     */
    public static <T> LinkedList<T> linkedList(T... values) {
        return linkedList(values == null ? Collections.<T>emptyList() : Arrays.asList(values));
    }

    /**
     * Vytvori {@link LinkedList} ze vsech zadanych hodnot, ktere nejsou {@code null}.
     * Nikdy nevraci {@code null} a nevyhazuje NPE.
     */
    public static <T> LinkedList<T> linkedList(Collection<? extends T> values) {
        LinkedList<T> result = new LinkedList<T>();
        if (values != null) {
            for (T value : values) {
                if (value != null) {
                    result.add(value);
                }
            }
        }
        return result;
    }

    /**
     * Spojeni dvou poli do jednoho
     * @param newArrayType typova trida nove vznikleho pole. Nekdy chci proste pole typu Oject, i kdyz
     * do nej budu sazet jine datove typy. Nemuzu se proto spolehnout ze typ pole a je ten samej co typ
     * pole b, staci mi ze oba typy poli extenduji U.
     */
    public static <T, U> U[] concat(Class<U> newArrayType, T[] a, T[] b) {
        final int alen = a.length;
        final int blen = b.length;
        final U[] result = (U[]) java.lang.reflect.Array.
                newInstance(newArrayType, alen + blen);
        System.arraycopy(a, 0, result, 0, alen);
        System.arraycopy(b, 0, result, alen, blen);
        return result;
    }
}
